
#include <vtkDelimitedTextReader.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkIntArray.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkParallelCoordinatesRepresentation.h>
#include <vtkParallelCoordinatesView.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkStringArray.h>
#include <vtkTable.h>

#include <vtksys/SystemTools.hxx>

int main(int argc, char* argv[])
{
    vtkNew<vtkNamedColors> colors;

    vtkSmartPointer<vtkTable> table = vtkSmartPointer<vtkTable>::New();
    std::string title;

    if (argc > 1)
    {
        std::string inputFilename;
        inputFilename = argv[1];
        vtkNew<vtkDelimitedTextReader> reader;
        reader->SetFileName(inputFilename.c_str());
        reader->SetHaveHeaders(1);
        reader->DetectNumericColumnsOn();
        reader->SetFieldDelimiterCharacters(",");
        reader->Update();
        table = reader->GetOutput();
        title = vtksys::SystemTools::GetFilenameWithoutExtension(
                vtksys::SystemTools::GetFilenameName(inputFilename));
    }
    else
    {
        title = "Generated Data";
        vtkNew<vtkIntArray> array1;
        array1->SetName("Array1");
        array1->SetNumberOfComponents(1);
        array1->InsertNextValue(0);
        array1->InsertNextValue(1);
        array1->InsertNextValue(2);
        array1->InsertNextValue(3);
        array1->InsertNextValue(4);

        vtkNew<vtkFloatArray> array2;
        array2->SetName("Array2");
        array2->SetNumberOfComponents(1);
        array2->InsertNextValue(-0);
        array2->InsertNextValue(-1);
        array2->InsertNextValue(-2);
        array2->InsertNextValue(-3);
        array2->InsertNextValue(-4);

        vtkNew<vtkDoubleArray> array3;
        array3->SetName("Array3");
        array3->SetNumberOfComponents(1);
        array3->InsertNextValue(0);
        array3->InsertNextValue(1);
        array3->InsertNextValue(4);
        array3->InsertNextValue(9);
        array3->InsertNextValue(16);

        table->AddColumn(array1);
        table->AddColumn(array2);
        table->AddColumn(array3);
    }

    vtkNew<vtkPolyData> polydata;

    for (vtkIdType i = 0; i < table->GetNumberOfColumns(); ++i)
    {
        if (dynamic_cast<vtkStringArray*>(table->GetColumn(i)))
        {
            std::cout << i << " is "
                      << "StringArray named ";
        }
        else if (dynamic_cast<vtkDoubleArray*>(table->GetColumn(i)))
        {
            std::cout << i << " is "
                      << "DoubleArray named ";
        }
        else if (dynamic_cast<vtkFloatArray*>(table->GetColumn(i)))
        {
            std::cout << i << " is "
                      << "DoubleArray named ";
        }
        else if (dynamic_cast<vtkIntArray*>(table->GetColumn(i)))
        {
            std::cout << i << " is "
                      << "IntArray named ";
        }
        else
        {
            std::cout << i << " is "
                      << "Unknown type named ";
        }
        std::cout << "\"" << table->GetColumn(i)->GetName() << "\"" << std::endl;

        polydata->GetPointData()->AddArray(table->GetColumn(i));
    }

    vtkNew<vtkParallelCoordinatesRepresentation> rep;
    rep->SetInputData(polydata);

    // List all of the attribute arrays you want plotted in parallel coordinates
    // Set up the parallel coordinates Representation to be used in the View
    for (vtkIdType i = 0; i < table->GetNumberOfColumns(); ++i)
    {
        if (dynamic_cast<vtkStringArray*>(table->GetColumn(i)))
        {
            continue;
        }
        else
        {
            rep->SetInputArrayToProcess(i, 0, 0, 0, table->GetColumn(i)->GetName());
        }
    }
    rep->UseCurvesOn();
    rep->SetFontSize(.5);
    rep->SetPlotTitle(title.c_str());
    rep->SetLineOpacity(0.5);
    rep->SetLineColor(colors->GetColor3d("Gold").GetData());
    rep->SetAxisColor(colors->GetColor3d("OrangeRed").GetData());
    rep->SetAxisLabelColor(colors->GetColor3d("Yellow").GetData());

    // Set up the Parallel Coordinates View and hook in the Representation
    vtkNew<vtkParallelCoordinatesView> view;
    view->SetRepresentation(rep);
    view->SetInspectMode(1);
    view->SetDisplayHoverText(1);

    // Brush Mode determines the type of interaction you perform to select data
    view->SetBrushModeToLasso();
    view->SetBrushOperatorToReplace();

    // Set up render window
    view->GetRenderWindow()->SetSize(600, 300);
    view->GetRenderer()->SetBackground2(
            colors->GetColor3d("MidnightBlue").GetData());
    view->GetRenderer()->SetBackground(colors->GetColor3d("Indigo").GetData());

    view->GetRenderWindow()->SetWindowName("ParallelCoordinatesView");
    view->ResetCamera();
    view->Render();

    // Start interaction event loop
    view->GetInteractor()->Start();

    return EXIT_SUCCESS;
}